/*
 * JPanelProgrammerEditor.java
 *
 * Copyright 2011 Universidad Francisco de Paula Santander (UFPS).
 * Todos los Derechos Reservados.
 * Created on 16/11/2010, 11:37:05 PM
 */

/*
 * A simple text editor that demonstrates the integration of the
 * com.Ostermiller.Syntax Syntax Highlighting package with a text editor.
 * Copyright (C) 2001 Stephen Ostermiller
 * http://ostermiller.org/contact.pl?regarding=Syntax+Highlighting
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * See COPYING.TXT for details.
 */

package com.ufps.cuatro.vistas;



import java.awt.Color;
import java.awt.Insets;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JTextPane;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

import org.jdesktop.application.Action;

import com.Ostermiller.Syntax.Lexer.*;

/**
 *<i><b>JPanelProgrammerEditor:</b></i> @DefinicionClase
 * @author Carlos A. Torres C. - Dolly E. Guerrero. <https://code.google.com/p/tesis4dor/>
 * @version 1.0 16/11/2010
 * @see javax.swing.JPanel
 */
public class JPanelProgrammerEditor extends javax.swing.JPanel {
    private static final long serialVersionUID = -5248261381376448425L;

    /**
     * the styled document that is the model for
     * the textPane
     */
    private HighLightedDocument document;
    /**
     * A reader wrapped around the document
     * so that the document can be fed into
     * the lexer.
     */
    private DocumentReader documentReader;
    /**
     * The lexer that tells us what colors different
     * words should be.
     */
    private Lexer syntaxLexer;
    /**
     * A thread that handles the actual coloring.
     */
    private Colorer colorer;
    /**
     * A lock for modifying the document, or for
     * actions that depend on the document not being
     * modified.
     */
    private final Object doclock = new Object();
    
    private FileReader objLeer = null;

    /** Creates new form JPanelProgrammerEditor */
    public JPanelProgrammerEditor() {
        initComponents();
        inicializarEditor("");
    }
    
    /**
     * @DefinicionMetodo
     * @param strCode @DefinicionParametro
     */
    public JPanelProgrammerEditor(String strCode) {
        initComponents();
        inicializarEditor(strCode);
    }   
    
    /**
     * @DefinicionMetodo
     * @param objFile @DefinicionParametro
     */
    public JPanelProgrammerEditor(File objFile) {        
        try {
            initComponents();
            objLeer = new FileReader(objFile);
            inicializarEditor();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(JPanelProgrammerEditor.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                objLeer.close();
            } catch (IOException ex) {
                Logger.getLogger(JPanelProgrammerEditor.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
    private void inicializarEditor() {
        // Create the document model.
        document = new HighLightedDocument();
        // Create the text pane and configure it.
        textPane.setDocument(document);
        textPane.setCaretPosition(0);
        textPane.setMargin(new Insets(5, 5, 5, 5));
        // Start the thread that does the coloring
        this.iniciarColorer();
        // Set up the hash table that contains the styles.
        this.initStyles();
        // create the new document.
        documentReader = new DocumentReader(document);
        
        // Put the initial text into the text pane and
        // set it's initial coloring style.
        this.initDocument();
        this.setVisible(true);
    }

    private void inicializarEditor(String strMsg) {
        // Create the document model.
        document = new HighLightedDocument();
        // Create the text pane and configure it.
        textPane.setDocument(document);
        textPane.setCaretPosition(0);
        textPane.setMargin(new Insets(5, 5, 5, 5));
        // Start the thread that does the coloring
        this.iniciarColorer();
        // Set up the hash table that contains the styles.
        this.initStyles();
        // create the new document.
        documentReader = new DocumentReader(document);
        // Put the initial text into the text pane and
        // set it's initial coloring style.
        this.initDocument(strMsg);
        this.setVisible(true);
    }

    private void iniciarColorer(){
        colorer = new Colorer();
        colorer.start();        
    }   

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jScrollPane2 = new javax.swing.JScrollPane();
        textPane = new javax.swing.JTextPane();
        jToolBar1 = new javax.swing.JToolBar();
        jToggleButton1 = new javax.swing.JToggleButton();

        setName("Form"); // NOI18N

        jScrollPane2.setName("jScrollPane2"); // NOI18N

        textPane.setName("textPane"); // NOI18N
        jScrollPane2.setViewportView(textPane);

        jToolBar1.setFloatable(false);
        jToolBar1.setRollover(true);
        jToolBar1.setName("jToolBar1"); // NOI18N

        javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance().getContext().getActionMap(JPanelProgrammerEditor.class, this);
        jToggleButton1.setAction(actionMap.get("editar")); // NOI18N
        jToggleButton1.setFocusable(false);
        jToggleButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        jToggleButton1.setName("jToggleButton1"); // NOI18N
        jToggleButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        jToolBar1.add(jToggleButton1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 432, Short.MAX_VALUE)
            .addGroup(layout.createSequentialGroup()
                .addGap(10, 10, 10)
                .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 412, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 47, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 222, Short.MAX_VALUE)
                .addContainerGap())
        );
    }// </editor-fold>//GEN-END:initComponents


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JToggleButton jToggleButton1;
    private javax.swing.JToolBar jToolBar1;
    private javax.swing.JTextPane textPane;
    // End of variables declaration//GEN-END:variables

    /**
     * Initialize the document with some default text and set
     * they initial type of syntax highlighting.
     */
    private void initDocument() {

        try {
            String initString = "";
            
            BufferedReader br = new BufferedReader(this.objLeer);

             // Lectura del fichero
             String linea;
             while((linea = br.readLine())!=null)
                initString += linea + "\n";

            syntaxLexer = new JavaLexer(documentReader);

            try {
                document.insertString(document.getLength(), initString, getStyle(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("TEXT")));
            } catch (BadLocationException ble) {
                System.err.println(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("COULDN'T INSERT INITIAL TEXT."));
            }
        } catch (IOException ex) {
            Logger.getLogger(JPanelProgrammerEditor.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void initDocument(String initString) {
        syntaxLexer = new JavaLexer(documentReader);
        try {
            document.insertString(document.getLength(), initString, getStyle(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("TEXT")));
        } catch (BadLocationException ble) {
            System.err.println(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("COULDN'T INSERT INITIAL TEXT."));
        }
    }

    /**
     * Create the styles and place them in the hash table.
     */
    private void initStyles(){
        SimpleAttributeSet style;

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, Color.black);
        StyleConstants.setBold(style, false);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("BODY"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, Color.blue);
        StyleConstants.setBold(style, true);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("TAG"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, Color.blue);
        StyleConstants.setBold(style, false);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("ENDTAG"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, Color.black);
        StyleConstants.setBold(style, false);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("REFERENCE"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, new Color(0xB03060)/*Color.maroon*/);
        StyleConstants.setBold(style, true);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("NAME"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, new Color(0xB03060)/*Color.maroon*/);
        StyleConstants.setBold(style, false);
        StyleConstants.setItalic(style, true);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("VALUE"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, Color.black);
        StyleConstants.setBold(style, true);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("TEXT"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, Color.blue);
        StyleConstants.setBold(style, false);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("RESERVEDWORD"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, Color.black);
        StyleConstants.setBold(style, false);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("IDENTIFIER"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, new Color(0xB03060)/*Color.maroon*/);
        StyleConstants.setBold(style, false);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("LITERAL"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, new Color(0x000080)/*Color.navy*/);
        StyleConstants.setBold(style, false);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("SEPARATOR"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, Color.black);
        StyleConstants.setBold(style, true);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("OPERATOR"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, Color.green.darker());
        StyleConstants.setBold(style, false);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("COMMENT"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, new Color(0xA020F0).darker()/*Color.purple*/);
        StyleConstants.setBold(style, false);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("PREPROCESSOR"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, Color.black);
        StyleConstants.setBold(style, false);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("WHITESPACE"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, Color.red);
        StyleConstants.setBold(style, false);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("ERROR"), style);

        style = new SimpleAttributeSet();
        StyleConstants.setFontFamily(style, java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("MONOSPACED"));
        StyleConstants.setFontSize(style, 11);
        StyleConstants.setBackground(style, Color.white);
        StyleConstants.setForeground(style, Color.orange);
        StyleConstants.setBold(style, false);
        StyleConstants.setItalic(style, false);
        styles.put(java.util.ResourceBundle.getBundle("com/ufps/cuatro/vistas/resources/JPanelProgrammerEditor").getString("UNKNOWN"), style);
    }

    /**
     * A hash table containing the text styles.
     * Simple attribute sets are hashed by name (String)
     */
    private Hashtable<String, SimpleAttributeSet> styles = new Hashtable<String, SimpleAttributeSet>();

    /**
     * retrieve the style for the given type of text.
     *
     * @param styleName the label for the type of text ("tag" for example) 
     * or null if the styleName is not known.
     * @return the style
     */
    private SimpleAttributeSet getStyle(String styleName){
        return styles.get(styleName);
    }

    /**
     * Run the Syntax Highlighting as a separate thread.
     * Things that need to be colored are messaged to the
     * thread and put in a list.
     */
    private class Colorer extends Thread {

        /**
         * Keep a list of places in the file that it is safe to restart the
         * highlighting.  This happens whenever the lexer reports that it has
         * returned to its initial state.  Since this list needs to be sorted
         * and we need to be able to retrieve ranges from it, it is stored in a
         * balanced tree.
         */
        @SuppressWarnings("unchecked")
        private TreeSet<DocPosition> iniPositions = new TreeSet<DocPosition>(new DocPositionComparator());

        /**
         * As we go through and remove invalid positions we will also be finding
		 * new valid positions.
		 * Since the position list cannot be deleted from and written to at the same
         * time, we will keep a list of the new positions and simply add it to the
         * list of positions once all the old positions have been removed.
         */
    	private HashSet<DocPosition> newPositions = new HashSet<DocPosition>();

        /**
         * A simple wrapper representing something that needs to be colored.
         * Placed into an object so that it can be stored in a Vector.
         */
        private class RecolorEvent {
            public int position;
            public int adjustment;
            public RecolorEvent(int position, int adjustment){
                this.position = position;
                this.adjustment = adjustment;
            }
        }

        /**
         * Vector that stores the communication between the two threads.
         */
        private volatile Vector<RecolorEvent> v = new Vector<RecolorEvent>();

        /**
         * The amount of change that has occurred before the place in the
         * document that we are currently highlighting (lastPosition).
         */
        private volatile int change = 0;

        /**
         * The last position colored
         */
        private volatile int lastPosition = -1;

        private volatile boolean asleep = false;

        /**
         * When accessing the vector, we need to create a critical section.
         * we will synchronize on this object to ensure that we don't get
         * unsafe thread behavior.
         */
        private final Object lock = new Object();

        /**
         * Tell the Syntax Highlighting thread to take another look at this
         * section of the document.  It will process this as a FIFO.
         * This method should be done inside a doclock.
         */
        public void color(int position, int adjustment){
            // figure out if this adjustment effects the current run.
            // if it does, then adjust the place in the document
            // that gets highlighted.
            if (position < lastPosition){
                if (lastPosition < position - adjustment){
                    change -= lastPosition - position;
				} else {
                	change += adjustment;
                }
            }
            synchronized(lock){
                v.add(new RecolorEvent(position, adjustment));
                if (asleep){
                    this.interrupt();
                }
            }
        }

        /**
         * The colorer runs forever and may sleep for long
         * periods of time.  It should be interrupted every
         * time there is something for it to do.
         */
        @Override
        public void run(){
            int position = -1;
            int adjustment = 0;
            // if we just finish, we can't go to sleep until we
            // ensure there is nothing else for us to do.
            // use try again to keep track of this.
            boolean tryAgain = false;
            for (;;){  // forever
                synchronized(lock){
                    if (v.size() > 0){
                        RecolorEvent re = v.elementAt(0);
                        v.removeElementAt(0);
                        position = re.position;
                        adjustment = re.adjustment;
                    } else {
                        tryAgain = false;
                        position = -1;
                        adjustment = 0;
                    }
                }
                if (position != -1){
                    SortedSet workingSet;
                    Iterator workingIt;
                    DocPosition startRequest = new DocPosition(position);
                    DocPosition endRequest = new DocPosition(position + ((adjustment>=0)?adjustment:-adjustment));
                    DocPosition  dp;
                    DocPosition dpStart = null;
                    DocPosition dpEnd = null;

                    // find the starting position.  We must start at least one
                    // token before the current position
                    try {
                        // all the good positions before
                        workingSet = iniPositions.headSet(startRequest);
                        // the last of the stuff before
                        dpStart = ((DocPosition)workingSet.last());
                    } catch (NoSuchElementException x){
                        // if there were no good positions before the requested start,
                        // we can always start at the very beginning.
                        dpStart = new DocPosition(0);
                    }

                    // if stuff was removed, take any removed positions off the list.
                    if (adjustment < 0){
                        workingSet = iniPositions.subSet(startRequest, endRequest);
                        workingIt = workingSet.iterator();
                        while (workingIt.hasNext()){
                            workingIt.next();
                            workingIt.remove();
                        }
                    }

                    // adjust the positions of everything after the insertion/removal.
                    workingSet = iniPositions.tailSet(startRequest);
                    workingIt = workingSet.iterator();
                    while (workingIt.hasNext()){
                        ((DocPosition)workingIt.next()).adjustPosition(adjustment);
                    }

                    // now go through and highlight as much as needed
                    workingSet = iniPositions.tailSet(dpStart);
                    workingIt = workingSet.iterator();
                    dp = null;
                    if (workingIt.hasNext()){
                        dp = (DocPosition)workingIt.next();
                    }
                    try {
                        Token t;
                        boolean done = false;
                        dpEnd = dpStart;
                        synchronized (doclock){
                            // we are playing some games with the lexer for efficiency.
                            // we could just create a new lexer each time here, but instead,
                            // we will just reset it so that it thinks it is starting at the
                            // beginning of the document but reporting a funny start position.
                            // Reseting the lexer causes the close() method on the reader
                            // to be called but because the close() method has no effect on the
                            // DocumentReader, we can do this.
                            syntaxLexer.reset(documentReader, 0, dpStart.getPosition(), 0);
                            // After the lexer has been set up, scroll the reader so that it
                            // is in the correct spot as well.
                            documentReader.seek(dpStart.getPosition());
                            // we will highlight tokens until we reach a good stopping place.
                            // the first obvious stopping place is the end of the document.
                            // the lexer will return null at the end of the document and wee
                            // need to stop there.
                            t = syntaxLexer.getNextToken();
                        }
                        newPositions.add(dpStart);
                        while (!done && t != null){
                            // this is the actual command that colors the stuff.
                            // Color stuff with the description of the style matched
                            // to the hash table that has been set up ahead of time.
                            synchronized (doclock){
                                if (t.getCharEnd() <= document.getLength()){
                                    document.setCharacterAttributes(
                                        t.getCharBegin() + change,
                                        t.getCharEnd()-t.getCharBegin(),
                                        getStyle(t.getDescription()),
                                        true
                                    );
                                    // record the position of the last bit of text that we colored
                                    dpEnd = new DocPosition(t.getCharEnd());
                                }
                                lastPosition = (t.getCharEnd() + change);
                            }
                            // The other more complicated reason for doing no more highlighting
                            // is that all the colors are the same from here on out anyway.
                            // We can detect this by seeing if the place that the lexer returned
                            // to the initial state last time we highlighted is the same as the
                            // place that returned to the initial state this time.
                            // As long as that place is after the last changed text, everything
                            // from there on is fine already.
                            if (t.getState() == Token.INITIAL_STATE){
                                //System.out.println(t);
                                // look at all the positions from last time that are less than or
                                // equal to the current position
                                while (dp != null && dp.getPosition() <= t.getCharEnd()){
                                    if (dp.getPosition() == t.getCharEnd() && dp.getPosition() >= endRequest.getPosition()){
                                        // we have found a state that is the same
										done = true;
                                        dp = null;
                                    } else if (workingIt.hasNext()){
                                        // didn't find it, try again.
                                        dp = (DocPosition)workingIt.next();
                                    } else {
                                        // didn't find it, and there is no more info from last
                                        // time.  This means that we will just continue
                                        // until the end of the document.
                                        dp = null;
                                    }
                                }
                                // so that we can do this check next time, record all the
                                // initial states from this time.
                                newPositions.add(dpEnd);
                            }
                            synchronized (doclock){
                                t = syntaxLexer.getNextToken();
                            }
                        }

                        // remove all the old initial positions from the place where
                        // we started doing the highlighting right up through the last
                        // bit of text we touched.
                        workingIt = iniPositions.subSet(dpStart, dpEnd).iterator();
                        while (workingIt.hasNext()){
                            workingIt.next();
                            workingIt.remove();
                        }

                        // Remove all the positions that are after the end of the file.:
                        workingIt = iniPositions.tailSet(new DocPosition(document.getLength())).iterator();
                        while (workingIt.hasNext()){
                            workingIt.next();
                            workingIt.remove();
                        }

                        // and put the new initial positions that we have found on the list.
                        iniPositions.addAll(newPositions);
                        newPositions.clear();

                        /*workingIt = iniPositions.iterator();
                        while (workingIt.hasNext()){
                            System.out.println(workingIt.next());
                        }

                        System.out.println("Started: " + dpStart.getPosition() + " Ended: " + dpEnd.getPosition());*/
                    } catch (IOException x){
                    }
                    synchronized (doclock){
                        lastPosition = -1;
                        change = 0;
                    }
                    // since we did something, we should check that there is
                    // nothing else to do before going back to sleep.
                    tryAgain = true;
                }
                asleep = true;
                if (!tryAgain){
                    try {
                        Colorer.sleep (0xffffff);
                    } catch (InterruptedException x){
                    }

                }
                asleep = false;
            }
        }
    }

    /**
     * Color or recolor the entire document
     */
    public void colorAll(){
        color(0, document.getLength());
    }

    /**
     * Color a section of the document.
     * The actual coloring will start somewhere before
     * the requested position and continue as long
     * as needed.
     *
     * @param position the starting point for the coloring.
     * @param adjustment amount of text inserted or removed
     *    at the starting point.
     */
    public void color(int position, int adjustment){
        colorer.color(position, adjustment);
    }

    /**
     * A reader interface for an abstract document.  Since
     * the syntax highlighting packages only accept Stings and
     * Readers, this must be used.
     * Since the close() method does nothing and a seek() method
     * has been added, this allows us to get some performance
     * improvements through reuse.  It can be used even after the
     * lexer explicitly closes it by seeking to the place that
     * we want to read next, and reseting the lexer.
     */
    class DocumentReader extends Reader {

        /**
         * Modifying the document while the reader is working is like
         * pulling the rug out from under the reader.  Alerting the
         * reader with this method (in a nice thread safe way, this
         * should not be called at the same time as a read) allows
         * the reader to compensate.
         */
        public void update(int position, int adjustment){
            if (position < this.position){
                if (this.position < position - adjustment){
                    this.position = position;
                } else {
                    this.position += adjustment;
                }
            }
        }

        /**
         * Current position in the document. Incremented
         * whenever a character is read.
         */
        private long position = 0;

        /**
         * Saved position used in the mark and reset methods.
         */
        private long mark = -1;

        /**
         * The document that we are working with.
         */
        private AbstractDocument document;

        /**
         * Construct a reader on the given document.
         *
         * @param document the document to be read.
         */
        public DocumentReader(AbstractDocument document){
            this.document = document;
        }

        /**
         * Has no effect.  This reader can be used even after
         * it has been closed.
         */
        public void close() {
        }

        /**
         * Save a position for reset.
         *
         * @param readAheadLimit ignored.
         */
        @Override
        public void mark(int readAheadLimit){
            mark = position;
        }

        /**
         * This reader support mark and reset.
         *
         * @return true
         */
        @Override
        public boolean markSupported(){
            return true;
        }

        /**
         * Read a single character.
         *
         * @return the character or -1 if the end of the document has been reached.
         */
        @Override
        public int read(){
            if (position < document.getLength()){
                try {
                    char c = document.getText((int)position, 1).charAt(0);
                    position++;
                    return c;
                } catch (BadLocationException x){
                    return -1;
                }
            } else {
                return -1;
            }
        }

        /**
         * Read and fill the buffer.
         * This method will always fill the buffer unless the end of the document is reached.
         *
         * @param cbuf the buffer to fill.
         * @return the number of characters read or -1 if no more characters are available in the document.
         */
        @Override
        public int read(char[] cbuf){
            return read(cbuf, 0, cbuf.length);
        }

        /**
         * Read and fill the buffer.
         * This method will always fill the buffer unless the end of the document is reached.
         *
         * @param cbuf the buffer to fill.
         * @param off offset into the buffer to begin the fill.
         * @param len maximum number of characters to put in the buffer.
         * @return the number of characters read or -1 if no more characters are available in the document.
         */
        public int read(char[] cbuf, int off, int len){
            if (position < document.getLength()){
                int length = len;
                if (position + length >= document.getLength()){
                    length = document.getLength() - (int)position;
                }
                if (off + length >= cbuf.length){
                    length = cbuf.length - off;
                }
                try {
                    String s = document.getText((int)position, length);
                    position += length;
                    for (int i=0; i<length; i++){
                        cbuf[off+i] = s.charAt(i);
                    }
                    return length;
                } catch (BadLocationException x){
                    return -1;
                }
            } else {
                return -1;
            }
        }

        /**
         * @return true
         */
        @Override
        public boolean ready() {
            return true;
        }

        /**
         * Reset this reader to the last mark, or the beginning of the document if a mark has not been set.
         */
        @Override
        public void reset(){
            if (mark == -1){
                position = 0;
            } else {
                position = mark;
            }
            mark = -1;
        }

        /**
         * Skip characters of input.
         * This method will always skip the maximum number of characters unless
         * the end of the file is reached.
         *
         * @param n number of characters to skip.
         * @return the actual number of characters skipped.
         */
        @Override
        public long skip(long n){
            if (position + n <= document.getLength()){
                position += n;
                return n;
            } else {
                long oldPos = position;
                position = document.getLength();
                return (document.getLength() - oldPos);
            }
        }

        /**
         * Seek to the given position in the document.
         *
         * @param n the offset to which to seek.
         */
        public void seek(long n){
            if (n <= document.getLength()){
                position = n;
            } else {
                position = document.getLength();
            }
        }
    }

    /**
     * A comparator appropriate for use with Collections of
     * DocPositions.
     */
    class DocPositionComparator implements Comparator{
        /**
         * Does this Comparator equal another?
         * Since all DocPositionComparators are the same, they
         * are all equal.
         *
         * @return true for DocPositionComparators, false otherwise.
         */
        @Override
        public boolean equals(Object obj){
            if (obj instanceof DocPositionComparator){
                return true;
            } else {
                return false;
            }
        }

        /**
         * Compare two DocPositions
         *
         * @param o1 first DocPosition
         * @param o2 second DocPosition
         * @return negative if first < second, 0 if equal, positive if first > second
         */
        public int compare(Object o1, Object o2){
            if (o1 instanceof DocPosition && o2 instanceof DocPosition){
                DocPosition d1 = (DocPosition)(o1);
                DocPosition d2 = (DocPosition)(o2);
                return (d1.getPosition() - d2.getPosition());
            } else if (o1 instanceof DocPosition){
                return -1;
            } else if (o2 instanceof DocPosition){
                return 1;
            } else if (o1.hashCode() < o2.hashCode()){
                return -1;
            } else if (o2.hashCode() > o1.hashCode()){
                return 1;
            } else {
                return 0;
            }
        }

        @Override
        public int hashCode() {
            int hash = 7;
            return hash;
        }
    }

    /**
     * A wrapper for a position in a document appropriate for storing
     * in a collection.
     */
    class DocPosition {

        /**
         * The actual position
         */
        private int position;

        /**
         * Get the position represented by this DocPosition
         *
         * @return the position
         */
        int getPosition(){
            return position;
        }

        /**
         * Construct a DocPosition from the given offset into the document.
         * @param position The position this DocObject will represent
         */
        public DocPosition(int position){
            this.position = position;
        }

        /**
         * Adjust this position.
         * This is useful in cases that an amount of text is inserted
         * or removed before this position.
         *
         * @param adjustment amount (either positive or negative) to adjust this position.
         * @return the DocPosition, adjusted properly.
         */
        public DocPosition adjustPosition(int adjustment){
            position += adjustment;
            return this;
        }

        /**
         * Two DocPositions are equal iff they have the same internal position.
         *
         * @return if this DocPosition represents the same position as another.
         */
        @Override
        public boolean equals(Object obj){
            if (obj instanceof DocPosition){
                DocPosition d = (DocPosition)(obj);
                if (this.position == d.position){
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            int hash = 3;
            hash = 37 * hash + this.position;
            return hash;
        }

        /**
         * A string representation useful for debugging.
         * @return A string representing the position.
         */
        @Override
        public String toString(){
            return "" + position;
        }
    }

    /**
     * Just like a DefaultStyledDocument but intercepts inserts and
     * removes to color them.
     */
    private class HighLightedDocument extends DefaultStyledDocument {
        private static final long serialVersionUID = -4299843582155414471L;
        @Override
        public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
            synchronized (doclock){
                super.insertString(offs, str, a);
                color(offs, str.length());
                documentReader.update(offs, str.length());
            }
        }

        @Override
        public void remove(int offs, int len) throws BadLocationException {
            synchronized (doclock){
                super.remove(offs, len);
                color(offs, -len);
                documentReader.update(offs, -len);
            }
        }
    }

    /**
     * @DefinicionMetodo 
     */
    @Action
    public void loadAvanzado() {
    }

    /**
     * @DefinicionMetodo
     * @return
     */
    public Lexer getSyntaxLexer() {
        return syntaxLexer;
    }

    /**
     * @DefinicionMetodo
     * @return
     */
    public JTextPane getTextPane() {
        return textPane;
    }
    
    /**
     * @DefinicionMetodo
     * @return
     */
    public String getText(){
        return this.textPane.getText();
    }

    /**
     * @DefinicionMetodo
     */
    @Action
    public void editar() {
        this.textPane.setEditable(this.jToggleButton1.isSelected());
    }


}
